/*
 * Gedigarr - Falling block game.
 * Copyright (C) 2010  BoredCorp
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */
package gedigarr.game;

import gedigarr.graphics.GameGraphics;
import gedigarr.input.InputHandler;
import gedigarr.timers.GameTimer;
import java.awt.Image;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import javax.swing.JFrame;
import putil.Putil;

/**
 *
 * @author JC
 */
public class Game implements Runnable, WindowListener {

  private static final long ONE_SECOND = 1000000000;
  private static final long ONE_MILLISECOND = 1000000;
  private JFrame frame = new JFrame( "Gedigarr - Tetris Clone" );
  private Image buffer = null;
  private GameState state;
  private Thread thread;
  private GameGraphics graphics;
  private InputHandler input;
  private static final int GAME_WIDTH = 300;
  private static final int GAME_HEIGHT = 377;

  /**
   * Initiates the canvas, state, and starts the game thread.
   */
  public Game() {
    this.state = new GameState( GameState.STATE_MENU );
    this.graphics = new GameGraphics( this.state );
    this.thread = new Thread( this );
    this.input = InputHandler.getInstance();
    graphics.setSize( GAME_WIDTH, GAME_HEIGHT );
    this.frame.addKeyListener( InputHandler.getInstance() );
    this.graphics.addKeyListener( InputHandler.getInstance() );
    this.frame.addWindowListener( this );

    this.frame.getContentPane().add( getGraphics() );
    this.frame.pack();
    this.frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
  }
  /*
  public Canvas getCanvas() {
  return this.graphics.getCanvas();
  }
   */

  public void start() {
    frame.setVisible( true );
    graphics.initialise();
    this.thread.start();
  }

  public void run() {
    long temp = 0l;
    int UPDATES_PER_SECOND = 30;
    int AVG_SAMPLES = 5;
    int desiredUps = (int) (ONE_SECOND / UPDATES_PER_SECOND);
    int fpsCount = 0;
    int upsCount = 0;

    int[] fps = new int[ AVG_SAMPLES ];
    int[] ups = new int[ AVG_SAMPLES ];
    for ( int i = 0; i < AVG_SAMPLES; i++ ) {
      fps[i] = UPDATES_PER_SECOND;
      ups[i] = UPDATES_PER_SECOND;
    }
    int upsIndex = 0;
    GameTimer tUpdate = new GameTimer( 0, 1000 );
    long tlUpdate = 0, tlPaint = 0, tlSleep = 0;
    long lastUpdate = curTime();
    long lastSleep = curTime();

    while ( state.getState() != GameState.STATE_EXITING ) {
      tlUpdate = curTime() - lastUpdate;
      state.update( input, tlUpdate / ONE_MILLISECOND );
      temp = curTime();
      tUpdate.inc( temp - lastUpdate );
      lastUpdate = temp;
      upsCount++;
      if ( tUpdate.hasTicked() ) {
        tUpdate.tick();
        upsIndex = Putil.wrapIndex( AVG_SAMPLES, upsIndex++ );
        ups[upsIndex] = upsCount;
        upsCount = 0;
      }

      graphics.paint();
      fpsCount++;

      try {
        tlSleep = curTime() - lastSleep;
        if ( desiredUps > tlSleep ) {
          Thread.sleep( (desiredUps - tlSleep) / 1000000 );
        } else {
          Thread.sleep( 5 );
        }
        lastSleep = curTime();
      } catch ( InterruptedException ex ) {
        ex.printStackTrace();
      }
    }

    frame.dispose();
  }

  private long curTime() {
    return System.nanoTime();
  }

  public GameGraphics getGraphics() {
    return this.graphics;
  }

  public void windowOpened( WindowEvent e ) {
  }

  public void windowClosing( WindowEvent e ) {
    state.requestStateChange( GameState.STATE_EXITING );
  }

  public void windowClosed( WindowEvent e ) {
  }

  public void windowIconified( WindowEvent e ) {
  }

  public void windowDeiconified( WindowEvent e ) {
  }

  public void windowActivated( WindowEvent e ) {
  }

  public void windowDeactivated( WindowEvent e ) {
    if ( state.getState() == GameState.STATE_RUNNING ) {
      state.requestStateChange( GameState.STATE_PAUSED );
    }
  }
}
